package com.gcsoftware.gateway.filters.http.requst;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.gcsoftware.gateway.GWImpl.GwAccessControl;
import com.gcsoftware.gateway.confLoader.CfgLoader;
import com.gcsoftware.gateway.confLoader.LoadGWRbacParam;
import com.gcsoftware.gateway.redis.LoadXml2Redis;
import com.gcsoftware.gateway.redis.RedisUtils;
import com.gcsoftware.gateway.utils.FileUtils;
import com.gcsoftware.gateway.utils.exception.UnsupportedCharsetException;
import com.gcsoftware.gateway.utils.httpObject.BrowserMobHttpUtil;
import com.gcsoftware.gateway.utils.regrex.RegexUtils;
import com.gcsoftware.gw.TokenInfo;
import com.gcsoftware.gw.beans.GwAccessRole;
import com.gcsoftware.gw.xml.GwXmlUtil;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.soap.SOAPMessage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by yinpeng on 18/1/26.
 */
public class ReqContentPull
{
    private final Logger log = LoggerFactory.getLogger(ReqContentPull.class);

    private static final String FILE_UPLOAD_VIA = "DsSolutionUpload";

    private RedisUtils redisUtils =getInstance();
    public RedisUtils getInstance() {
        if (null == redisUtils) {
            synchronized (RedisUtils.class) {
                if (null == redisUtils) {
                    redisUtils = new RedisUtils();
                }
            }
        }
        return redisUtils;
    }

    private String getMethodNameFromReq(String text) {
        log.debug("1-------Start to get targetMethod from request content.");
        log.debug("[{}]" , text);
        String soapPrefix = RegexUtils.getSubUtilSimple(text,"<(.*?):Envelope");
        log.debug("2-------Get targetMethod from request content, the prefix of soap request is [{}]" , soapPrefix);
        if (text.contains("<" + soapPrefix + ":Body>")) {
            log.debug("3-------Request content contais soap prefix [{}]" , soapPrefix);
            text = RegexUtils.getSubUtilSimple(text,"<" + soapPrefix + ":Body>(.*?)" + soapPrefix + ":Body>");
            log.debug("4-------Get soap body from request content. body is [{}]" , text);
            if (StringUtils.isNotBlank(text)) {
                text = text.substring(text.lastIndexOf(":")+1,text.lastIndexOf(">"));
                log.debug("Get targetMethod from request content. methodName is [{}]" , text);
                return text;
            }
        }
        return null;
    }
    /*private String getEndpointFromReq(String text,String targetMethod) {
        log.debug("Start to get endPoint from request content.");
        if (text.contains(":" + targetMethod)) {
            String aliasName = text.substring(0,text.indexOf(":"+targetMethod));
            if (StringUtils.isNotBlank(aliasName)) {
                aliasName = aliasName.substring(aliasName.lastIndexOf("<")+1);
                text = RegexUtils.getSubUtilSimple(text,"xmlns:" + aliasName +"=\"(.*?)/\">");
                log.debug("Get endPoint from request content. endPoint is [{}]" , text);
                return text + "/";
            }
        }
        return null;
    }*/
    private String getServiceIdFromReq(String text,String accessRowServ) {
        if (text.contains("<" + accessRowServ + ">")) {
            String serviceId = RegexUtils.getSubUtilSimple(text,"<" + accessRowServ +">(.*?)</" + accessRowServ + ">");
            return serviceId;
        }
        return null;
    }

    private String getRoleIdFromReq(String text) {
        if (text.contains("<role_id>")) {
            return RegexUtils.getSubUtilSimple(text,"<role_id>(.*?)</role_id>");
        }
        return null;
    }
    public Map<String, Object> getReqRole(FullHttpRequest req, Map<String , Object> map)
    {
        try
        {
            log.debug("Get role from request content.");
            ByteArrayOutputStream rawResponseContents = new ByteArrayOutputStream();
            byte[] content = BrowserMobHttpUtil.extractReadableBytes(req.content());

            rawResponseContents.write(content);

            rawResponseContents = BrowserMobHttpUtil.decompressContents(req, rawResponseContents);
            String contentType = HttpHeaders.getHeader(req, HttpHeaders.Names.CONTENT_TYPE);
            String textafter;
            Charset charset=null;
            try
            {
                charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentType);
            }
            catch (UnsupportedCharsetException e)
            {
                log.warn(
                        "Found unsupported character set in Content-Type header '{}' in HTTP response from {}. Content will not be captured in HAR.",
                        contentType, req.getUri(), e);

            }

            if (charset == null) {
                // no charset specified, so use the default -- but log a message since this might not encode the data correctly
                charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET;
            }
            String text = "";
            String rsp = null;
            if (BrowserMobHttpUtil.hasTextualContent(contentType))
            {

                text = BrowserMobHttpUtil.getContentAsString(rawResponseContents.toByteArray(), charset);
                SOAPMessage msg = null;

                if (ReqContentPull.FILE_UPLOAD_VIA.equals(HttpHeaders.getHeader(req, HttpHeaders.Names.VIA))) {
                    log.debug("upload access role xml files.");

                    FileUtils.writeFile(text, LoadGWRbacParam.getInstance().getXmlSavePath() + File.separator + getRoleIdFromReq(text) + ".xml");
                    try {
                        GwAccessRole role = (GwAccessRole) GwXmlUtil.createObject(GwAccessRole.class, text);
                        new LoadXml2Redis().setAccessRole2Redis(role,text);
                    } catch (Exception e) {
                        new LoadXml2Redis().init();
                    }
                    return null;
                }
                if (null != text && !"".equals(text)) {
                    text = text.replaceAll("\\n","");
                    map.put("reqHasContent" , true);
                    text = replc(text , "token");
                    String targetMethod = getMethodNameFromReq(text);
                   // String namespace = getEndpointFromReq(text,targetMethod);
                    //String endPoint = "http://" + HttpHeaders.getHeader(req, HttpHeaders.Names.HOST) +req.getUri();


                    TokenInfo tokenInfo = null;
                    String serviceId = "";

                    if (text.contains("<token>") && text.contains("</token>")) {
                        rsp = text.substring(text.indexOf("<token>") + 7 , text.indexOf("</token>"));
                        if (StringUtils.isNotBlank(rsp)) {
                            map.put("token" , rsp);
                            String tokenStr = redisUtils.getString(redisUtils.TOKEN_ROLE_PREFIX + rsp);
                            log.debug("get cached token-info from redis , token = [{}], value from redis is [{}]" , rsp , tokenStr);
                            if (StringUtils.isNotBlank(tokenStr)) {
                                tokenInfo = JSONObject.parseObject(tokenStr,TokenInfo.class);
                                log.debug("transfer cached value 2 token bean.");
                            }

                        }
                    }
                    if (StringUtils.isNotBlank(targetMethod) && null != tokenInfo) {
                        String endPoint = GwAccessControl.getGwAccessRoleEndpoint(tokenInfo.getRoleId(),req.getUri());
                        if (StringUtils.isNotBlank(endPoint)) {
                            String interfServ = GwAccessControl.getUnifyInterfServ(tokenInfo.getRoleId(), endPoint);
                            if (StringUtils.isNotBlank(interfServ)) {
                                serviceId = getServiceIdFromReq(text, interfServ);
                            } else {
                                serviceId = "";
                            }
                        }
                        map.put("targetMethod",targetMethod);
                        map.put("endPoint",endPoint);
                        map.put("serviceId",serviceId);
                        map.put("roleId",tokenInfo.getRoleId());
                        map.put("roleName",tokenInfo.getRoleName());
                        map.put("appId" , tokenInfo.getAppId());
                        map.put("appName" , tokenInfo.getAppName());
                        map.put("appIps" , tokenInfo.getAppIps());
                        map.put("senstiveIds",GwAccessControl.getAccessSensIds(tokenInfo.getRoleId()));
                        map.put("senstiveNames" , GwAccessControl.getAccessSensNames(tokenInfo.getRoleId()));

                    }
                }
                //@todo here to get what you want that need to analysis String by your self
                log.debug("*****RequestFullContent: {}",text);
            }

            return map;

        }
        catch (Exception e)
        {
            e.printStackTrace();
            log.warn("getContentAsString throw exception, will return original response!", e);
            return null;
        }
    }

    public String replc(String text, String arg) {

        Pattern test_ = Pattern.compile(arg,Pattern.CASE_INSENSITIVE);
        Matcher result_ = test_.matcher(text);

        text = result_.replaceAll(arg.toLowerCase());

        return text;
    }

}
